﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Web;
using OpenWaves.Web;

namespace OpenWaves.ImageTransformations.Web
{
    public class WebImageTransformationModuleImplementation : IWebImageTransformationService, IWebImageTransformationModuleImplementation
    {
        private readonly IWebImageTransformationService webImageTransformationService;
        private readonly IEnumerable<IImageTransformationUrlValidationRule> urlValidationRules;

        public WebImageTransformationModuleImplementation(IWebImageTransformationService webImageTransformationService) 
            : this(webImageTransformationService, new HmacImageTransformationUrlValidationRule())
        {}

        public WebImageTransformationModuleImplementation(IWebImageTransformationService webImageTransformationService, params IImageTransformationUrlValidationRule[] urlValidationRules)
        {
            Contract.Requires<ArgumentNullException>(webImageTransformationService != null);
            Contract.Requires<ArgumentNullException>(urlValidationRules != null);

            this.webImageTransformationService = webImageTransformationService;
            this.urlValidationRules = urlValidationRules;
        }

        public Url GetTransformedImageUrl(Url imageUrl, IImageTransformation transformation)
        {
            if (imageUrl is RelativeUrl)
            {
                var builder = imageUrl.GetBuilder();
                this.EncodeTransformationInUrl(transformation.ToString(), builder);
                return builder.ToUrl();
            }
            else
            {
                return this.webImageTransformationService.GetTransformedImageUrl(imageUrl, transformation);
            }            
        }

        public bool PostAuthorizeRequest(HttpContextBase context)
        {
            var url = context.Request.GetRawRelativeUrl();

            var transformation = DecodeTransformationFromUrl(url, context);
            if (transformation == null)
                return false;

            var transformedImageUrl = this.webImageTransformationService.GetTransformedImageUrl(
                url.Path.ToUrl(), 
                transformation);

            context.RewritePath(transformedImageUrl.ToString());
            context.Response.Cache.SetExpires(Time.Now + TimeSpan.FromDays(365));
            return true;
        }

        public void EncodeTransformationInUrl(string transformation, UrlBuilder urlBuilder)
        {
            urlBuilder.AddParameter("t", transformation);
            foreach (var rule in this.urlValidationRules)
            {
                rule.AugmentImageTransformationUrl(transformation, urlBuilder);
            }
        }

        public IImageTransformation DecodeTransformationFromUrl(Url url, HttpContextBase context)
        {
            string transformationString;
            if (url.Query.TryGetValue("t", out transformationString) == false)
                return null;

            if (String.IsNullOrEmpty(transformationString))
                return null;

            if (this.urlValidationRules.Any(rule=>rule.ValidateImageTransformationUrl(transformationString, url)) == false)
                return null;

            IImageTransformation transformation;
            if (ImageTransformation.TryParse(transformationString, out transformation) == false)
            {
                Log.Warning("Failed to parse transformation string: {0}", transformationString);
                return null;
            }

            string pixelRatioString;
            if (url.Query.TryGetValue("pr", out pixelRatioString))
            {
                double pixelRatio;
                if (Double.TryParse(pixelRatioString, out pixelRatio))
                {
                    var responsiveImageTransformation = transformation as IResponsiveImageTransformation;
                    if (responsiveImageTransformation != null)
                    {
                        pixelRatio = Math.Round(Math.Max(1.0, Math.Min(2.0, pixelRatio)), 1);

                        transformation = responsiveImageTransformation.Scale(pixelRatio);
                    }
                }
            }

            return transformation;
        }
    }
}
